package de.szut.logic;

import android.content.Context;
import de.szut.database.DatabaseHelper;
import de.szut.entity.*;
import de.szut.impl.AbilityTreeDaoImpl;
import de.szut.impl.BaseAbilityDaoImpl;
import de.szut.impl.CountryDaoImpl;
import de.szut.impl.UpgradeAbilityDaoImpl;
import de.szut.util.ModifierType;
import de.szut.util.SkillAbilityStatus;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * TODO
 * Singleton
 * Created by dzebcuks on 20.08.13.
 */
public class ModifierStore implements Serializable {

    private static ModifierStore instance = null;
    private DatabaseHelper helper;
    private UpgradeAbilityDaoImpl upgradeAbilityDao;
    private BaseAbilityDaoImpl baseAbilityDao;
    private AbilityTreeDaoImpl abilityTreeDao;
    private List<AbilityTree> availableTrees = new ArrayList<AbilityTree>();
    private List<AbilityUpgrade> availableUpgrades = new ArrayList<AbilityUpgrade>();
    private CountryDaoImpl countryDao;
    private Context context;
    public List<Country> allCountries = new ArrayList<Country>();
    public List<Ability> allAbilities = new ArrayList<Ability>();
    private List<BaseAbility> availableBaseAbilities = new ArrayList<BaseAbility>();
    private Points currentUserPoints = null;
    private int maxAbilityPoints = 0;

    private ModifierStore(Context ctx) {
        this.context = ctx;
        this.helper = DatabaseHelper.getInstance(ctx);
        this.baseAbilityDao = this.helper.getBaseAbilityDao();
        this.upgradeAbilityDao = this.helper.getUpgradeAbilityDao();
        this.abilityTreeDao = this.helper.getAbilityTreeDao();
        this.countryDao = this.helper.getCountryDao();
    }

    public static ModifierStore getInstance(Context ctx) {
        if (instance == null) {
            instance = new ModifierStore(ctx);
        }
        return instance;
    }

    /**
     * DATABASE INITIATE
     *
     * @param abilities
     * @param countries
     */
    public void initiateDatabase(List<Object> abilities, List<Object> countries) {
        try {
            this.initiateAbilityDatabase(abilities);
            this.initiateCountryDatabase(countries);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Initializes the database by adding all given abilities to it.
     *
     * @param abilities - the abilities to add
     */
    private void initiateAbilityDatabase(List<Object> abilities) throws SQLException {
        for (Object ability : abilities) {
            if (ability instanceof BaseAbility) {
                this.baseAbilityDao.create((BaseAbility) ability);
            } else {
                this.upgradeAbilityDao.create((AbilityUpgrade) ability);
            }
        }
    }

    /**
     * @param countries
     */
    public void initiateCountryDatabase(List<Object> countries) throws SQLException {
        for (Object country : countries) {
            if (country instanceof Country) {
                this.countryDao.create((Country) country);
            }
        }
    }

    // copy whole method
    public boolean abilityIsSkillAble(Ability abilityToSkill) {
        boolean isSkillAble = false;
        if (!abilityToSkill.isSkilled()) {
            if (abilityToSkill instanceof BaseAbility || ((AbilityUpgrade) abilityToSkill).isSkillable()) {
                if (this.getPointsForCurrentUser().getValue() > abilityToSkill.getPoints()) {
                    isSkillAble = true;
                }
            }
        }
        return isSkillAble;
    }

    public SkillAbilityStatus buyAbility(Ability ability) {
        SkillAbilityStatus success = SkillAbilityStatus.ALREADY_SKILLED;
        if (!ability.isSkilled()) {
            success = SkillAbilityStatus.DEPENDENT_ABILITY_IS_MISSING;
            if (ability instanceof BaseAbility || ((AbilityUpgrade) ability).isSkillable()) {
                success = SkillAbilityStatus.NOT_ENOUGH_POINTS;
                if (this.getPointsForCurrentUser().getValue() > ability.getPoints()) {
                    this.allAbilities.get(this.allAbilities.indexOf(ability)).setSkilled(true);
                    this.getPointsForCurrentUser().decreaseValue(ability.getPoints());
                    this.helper.getPointsDao().savePoints(this.getPointsForCurrentUser());
                    success = SkillAbilityStatus.SUCCESS;
                }
            }
        }
        return success;
    }

    public void loadAllAbilities() {
        this.loadAvailableBaseAbilities();
        this.loadAvailableUpgrades();
        if (this.allAbilities.isEmpty()){
            this.allAbilities.addAll(this.availableUpgrades);
        }
        if (maxAbilityPoints == 0) {
            for (Ability ability : allAbilities) {
                maxAbilityPoints += ability.getPoints();
            }
        }

    }

    private void loadAvailableBaseAbilities() {
        if (this.availableBaseAbilities.isEmpty()) {
            try {
                this.availableBaseAbilities.addAll(this.baseAbilityDao.queryForAll());
            } catch (SQLException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
    }

    /**
     * Loads all available ability upgrades into the array list
     */
    public void loadAvailableUpgrades() {
        try {
            if (this.availableUpgrades.isEmpty()) {
                this.availableUpgrades.addAll(this.upgradeAbilityDao.queryForAll());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Loads all available tree objects into the array list
     */
    public void loadAvailableTrees() {
        try {
            if (this.availableTrees.isEmpty()) {
                List<AbilityTree> trees = this.abilityTreeDao.queryForAll();
                for (AbilityTree t : trees) {
                    if (!this.availableTrees.contains(t))
                        this.availableTrees.add(t);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    /**
     * Generates the whole tree out of the ability tree object, which normally just points to
     * several base abilities.
     *
     * @param tree - the tree to build
     * @return
     */
    public List<Ability> buildFullTree(AbilityTree tree) {
        List<Ability> abilities = new ArrayList<Ability>();
        List<Ability> baseAbilities = new ArrayList<Ability>();
        abilities.addAll(tree.getAbilities());
        this.loadAvailableUpgrades();
        for (Ability ability : tree.getAbilities()) {
            baseAbilities.clear();
            baseAbilities.add(ability);
            while (!baseAbilities.isEmpty()) {
                Ability tmpBaseAbility = baseAbilities.get(0);
                for (AbilityUpgrade upgrade : this.availableUpgrades) {
                    if (upgrade.getDependentAbility().equals(tmpBaseAbility)) {
                        abilities.add(upgrade);
                        baseAbilities.add(upgrade);
                    }
                }
                baseAbilities.remove(tmpBaseAbility);
            }
        }
        return abilities;
    }

    /**
     * TODO: Check this shit out
     *
     * @param baseAbility
     * @return
     */
    public List<AbilityUpgrade> getUpgradesOf(BaseAbility baseAbility) {
        List<AbilityUpgrade> upgrades = new ArrayList<AbilityUpgrade>();
        this.loadAvailableUpgrades();
        Ability ability = baseAbility;
        boolean finished = false;
        while (!finished) {
            Ability checkAbility = ability;
            for (AbilityUpgrade upgrade : this.availableUpgrades) {
                if (upgrade.getDependentAbility().equals(ability) && !upgrades.contains(upgrade)) {
                    ability = upgrade;
                    upgrades.add(upgrade);
                }
            }
            //check if there probably cant be another upgrade
            if (checkAbility.equals(ability)) {
                finished = true;
            }
        }
        return upgrades;
    }

    /**
     * @param upgrade
     * @return
     */
    public BaseAbility getBaseAbilityOf(AbilityUpgrade upgrade) {
        boolean finished = false;
        Ability ability = upgrade;
        while (!finished) {
            ability = ((AbilityUpgrade) ability).getDependentAbility();

            if (ability instanceof BaseAbility) {
                finished = true;
            }
        }
        return (BaseAbility) ability;
    }

    public Double getModifierValueByType(ModifierType type) {
        Double modifierValue = new Double(0.0);
        this.loadAllAbilities();
        for (Ability ability : this.allAbilities) {
            if (ability.getModifier().getType().equals(type) && ability.isSkilled()) {
                modifierValue += ability.getModifier().getModifierValue().getValue();
            }
        }
        return modifierValue;
    }

    protected int countModifierValueByType(ModifierType type) {
        int modifierValue = 0;
        this.loadAllAbilities();
        for (Ability ability : this.allAbilities) {
            if (ability.getModifier().getType().equals(type)) {
                modifierValue += ability.getModifier().getModifierValue().getValue();
            }
        }
        return modifierValue;
    }

    public List<AbilityTree> getAvailableTrees() {
        return this.availableTrees;
    }

    public void loadAllCountries() {
        if (this.allCountries.isEmpty()) {
            try {
                this.allCountries.addAll(this.countryDao.queryForAll());
            } catch (SQLException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
    }

    public Points getPointsForCurrentUser() {
        if (this.currentUserPoints == null) {
            try {
                this.currentUserPoints = this.helper.getPointsDao().getPointsForCurrentUser();
            } catch (SQLException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
        return this.currentUserPoints;
    }

    protected DatabaseHelper getHelper() {
        return this.helper;
    }
}
